//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  NotificationItem.cs
//    
// Abstract:
//
//  This module defines the abstract NotificationItem class and several derived sub-classes.
//  These classes encapsulate all the necessary information for a notification.
//  
//--

using System;
using System.Collections.ObjectModel; 
using System.Diagnostics;
using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders
{
    /// <summary>
    /// Encapsulates all the necessary information for a notification.
    /// </summary>
    [Serializable]
    public abstract class NotificationItem : IComparable<NotificationItem>
    {
        private readonly NotificationId _id;
        private readonly string _emailAddress;
        private readonly DateTime _notificationTime;
        private readonly Context _context;
        private readonly string _displayName;

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="context">The logging context</param>
        /// <param name="notificationId">Mailbox item ID that generated this notification</param>
        /// <param name="emailAddress">Email address for the recipient of this notification</param>
        /// <param name="notificationTime">Time this notification should be delivered</param>
        /// <param name="displayName">Display name of the recipient of this notification</param>
        protected NotificationItem(Context context, NotificationId notificationId, string emailAddress, DateTime notificationTime, string displayName)
        {
            _context = context;
            _id = notificationId;
            _emailAddress = emailAddress;
            _notificationTime = notificationTime;
            _displayName = displayName;
        }

        /// <summary>
        /// Determines if the specified notification matches this one.
        /// </summary>
        /// <param name="notificationItem">Notification to be compared against</param>
        /// <remarks>
        /// This method only compares the ID. This is all that is needed to distinguish two notifications in the schedule.
        /// </remarks>
        /// <returns>
        /// True if the notifications are equal; false otherwise.
        /// </returns>
        public bool Equals(NotificationItem notificationItem)
        {
            Debug.Assert(notificationItem != null, "notificationItem != null");
            return _id.Equals(notificationItem._id);
        }

        /// <summary>
        /// Produces a string representation of this object.
        /// </summary>
        /// <remarks>
        /// This method is used in logging.
        /// </remarks>
        /// <returns>
        /// The string representation of this object.
        /// </returns>
        public override string ToString()
        {
            return base.ToString() + " ID = " + _id.ToString() + " Email = " + _emailAddress + 
                " Notification Time = " + _notificationTime.ToString() + " Display Name = " + _displayName;
        }

        /// <summary>
        /// Resolves the custom channels associated with this notification against the specified master list of channels.
        /// </summary>
        /// <param name="masterChannelList">Master list of channels</param>
        public abstract void ResolveCustomChannels(NotificationChannelList masterChannelList);

        /// <summary>
        /// The logging context associated with this notification.
        /// </summary>
        public Context Context
        {
            get
            {
                return _context;
            }
        }

        /// <summary>
        /// The unique ID for this notification.
        /// </summary>
        public NotificationId Id
        {
            get
            {
                return _id;
            }
        }

        /// <summary>
        /// The e-mail address for the recipient of this notification.
        /// </summary>
        public string EmailAddress
        {
            get
            {
                return _emailAddress;
            }
        }

        /// <summary>
        /// The time this notification is to be delivered.
        /// </summary>
        public DateTime NotificationTime
        {
            get
            {
                return _notificationTime;
            }
        }

        /// <summary>
        /// The display name of the recipient of the notification.
        /// </summary>
        public string DisplayName
        {
            get
            {
                return _displayName;
            }
        }

        /// <summary>
        /// Indicates if this notification is for JoinNow.
        /// </summary>
        public virtual bool IsJoinNow
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Indicates if the channels associated with this notification are enabled.
        /// </summary>
        public abstract bool IsEnabled
        {
            get;
        }

        #region IComparable

        /// <summary>
        /// Compares the time fields between this and the specified NotificationItem.
        /// </summary>
        /// <param name="other">NotificationItem to be compared to</param>
        /// <remarks>
        /// This method is used for sorting the notifications in the schedule based on time.
        /// </remarks>
        /// <returns>
        /// The comparison between this and the specified NotificationItem. 
        /// </returns>
        public int CompareTo(NotificationItem other)
        {
            Debug.Assert(_notificationTime != null, "_time should not be null");
            Debug.Assert(other != null, "other should not be null");

            return _notificationTime.CompareTo(other._notificationTime);
        }

        #endregion // IComparable
    }

    /// <summary>
    /// Encapsulates all the necessary information for a calendar notification.
    /// </summary>
    [Serializable]
    public class CalendarNotificationItem : NotificationItem
    {
        private readonly ReminderItem _reminderItem;
        private readonly bool _hasBody;
        private readonly string _location;
        private readonly string _message;
        private readonly DateTime _startTime;

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="context">The logging context</param>
        /// <param name="notificationId">A unique NotificationId for this notification</param>
        /// <param name="emailAddress">Email address for the recipient of this notification</param>
        /// <param name="notificationTime">Time this notification should be delivered</param>
        /// <param name="reminderItem">The reminder information for this notification</param>
        /// <param name="displayName">Display name of the recipient of this notification</param>
        /// <param name="hasBody">Whether or not the calendar item has a message body associated with it</param>
        /// <param name="location">The location that the appointment or meeting takes place at</param>
        /// <param name="message">Message to be included in this notification</param>
        /// <param name="startTime">The time this notification is actually relevant</param>
        public CalendarNotificationItem(Context context, NotificationId notificationId, string emailAddress, DateTime notificationTime, ReminderItem reminderItem,
            string displayName, bool hasBody, string location, string message, DateTime startTime)
            : base(context, notificationId, emailAddress, notificationTime, displayName)
        {
            Debug.Assert(reminderItem != null, "reminderItem != null");

            _reminderItem = reminderItem;
            _hasBody = hasBody;
            _location = location;
            _message = message;
            _startTime = startTime;
        }

        /// <summary>
        /// Produces a string representation of this object.
        /// </summary>
        /// <remarks>
        /// This method is used in logging.
        /// </remarks>
        /// <returns>
        /// The string representation of this object.
        /// </returns>
        public override string ToString()
        {
            return base.ToString() + " Has Body = " + _hasBody.ToString() + " Location = " + _location + " Message = " + _message + 
                " Start Time = " + _startTime.ToString();
        }

        /// <summary>
        /// Resolves the custom channels associated with this notification against the specified master list of channels.
        /// </summary>
        /// <param name="masterChannelList">Master list of channels</param>
        public override void ResolveCustomChannels(NotificationChannelList masterChannelList)
        {
            _reminderItem.NotificationChannelList.ResolveCustomChannels(masterChannelList);
        }

        /// <summary>
        /// The reminder information for this notification.
        /// </summary>
        public ReminderItem ReminderItem
        {
            get
            {
                return _reminderItem;
            }
        }

        /// <summary>
        /// Indicates whether or not the reminder has a message body associated with it.
        /// </summary>
        public bool HasBody
        {
            get
            {
                return _hasBody;
            }
        }

        /// <summary>
        /// Describes where an appointment or meeting takes place.
        /// </summary>
        public string Location
        {
            get
            {
                return _location;
            }
        }

        /// <summary>
        /// The message to be included with this notification.
        /// </summary>
        public string Message
        {
            get
            {
                return _message;
            }
        }

        /// <summary>
        /// The time this notification is actually relevant
        /// </summary>
        public DateTime StartTime
        {
            get
            {
                return _startTime;
            }
        }

        /// <summary>
        /// Indicates if the channels associated with this notification are enabled.
        /// </summary>
        public override bool IsEnabled
        {
            get
            {
                return _reminderItem.IsNotificationEnabled;
            }
        }
    }

    /// <summary>
    /// Encapsulates all the necessary information for a meeting notification.
    /// </summary>
    [Serializable]
    public class MeetingNotificationItem : CalendarNotificationItem
    {
        private readonly string _notificationOriginator;
        private readonly bool _isJoinNow;

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="context">The logging context</param>
        /// <param name="notificationId">A unique NotificationId for this notification</param>
        /// <param name="emailAddress">Email address for the recipient of this notification</param>
        /// <param name="notificationTime">Time this notification should be delivered</param>
        /// <param name="reminderItem">The reminder information for this notification</param>
        /// <param name="displayName">Display name of the recipient of this notification</param>
        /// <param name="hasBody">Whether or not the calendar item has a message body associated with it</param>
        /// <param name="location">The location that the appointment or meeting takes place at</param>
        /// <param name="message">Message to be included in this notification</param>
        /// <param name="startTime">The time this notification is actually relevant</param>
        /// <param name="notificationOriginator">The originator of this notification</param>
        /// <param name="isJoinNow">Indicates if this notification is for JoinNow</param>
        public MeetingNotificationItem(Context context, NotificationId notificationId, string emailAddress, DateTime notificationTime, ReminderItem reminderItem,
            string displayName, bool hasBody, string location, string message, DateTime startTime, string notificationOriginator, bool isJoinNow)
            : base(context, notificationId, emailAddress, notificationTime, reminderItem, displayName, hasBody, location, message, startTime)
        {
            _notificationOriginator = notificationOriginator;
            _isJoinNow = isJoinNow;
        }

        /// <summary>
        /// Produces a string representation of this object.
        /// </summary>
        /// <remarks>
        /// This method is used in logging.
        /// </remarks>
        /// <returns>
        /// The string representation of this object.
        /// </returns>
        public override string ToString()
        {
            return base.ToString() + " Originator = " + _notificationOriginator;
        }

        /// <summary>
        /// Resolves the custom channels associated with this notification against the specified master list of channels.
        /// </summary>
        /// <param name="masterChannelList">Master list of channels</param>
        public override void ResolveCustomChannels(NotificationChannelList masterChannelList)
        {
            if (_isJoinNow)
            {
                ReminderItem.JoinNowChannelList.ResolveCustomChannels(masterChannelList);
            }
            else
            {
                ReminderItem.NotificationChannelList.ResolveCustomChannels(masterChannelList);
            }
        }

        /// <summary>
        /// The originator of this notification.
        /// </summary>
        public string NotificationOriginator
        {
            get
            {
                return _notificationOriginator;
            }
        }

        /// <summary>
        /// Indicates if this notification is for JoinNow.
        /// </summary>
        public override bool IsJoinNow
        {
            get
            {
                return _isJoinNow;
            }
        }

        /// <summary>
        /// Indicates if the channels associated with this notification are enabled.
        /// </summary>
        public override bool IsEnabled
        {
            get
            {
                return _isJoinNow ? ReminderItem.IsJoinNowEnabled : ReminderItem.IsNotificationEnabled;
            }
        }
    }

    /// <summary>
    /// Encapsulates all the necessary information for a voicemail notification.
    /// </summary>
    [Serializable]
    public class VoicemailNotificationItem : NotificationItem
    {
        private readonly ReadOnlyCollection<ReminderItem> _reminderItems;
        private readonly bool _isWma;
        private readonly byte[] _voicemailAudio;
        private readonly string _voicemailCallerId;

        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="context">The logging context</param>
        /// <param name="notificationId">A unique NotificationId for this notification</param>
        /// <param name="emailAddress">Email address for the recipient of this notification</param>
        /// <param name="reminderItems">The reminder information for this notification</param>
        /// <param name="displayName">Display name of the recipient of this notification</param>
        /// <param name="notificationTime">Time this notification should be delivered</param>
        /// <param name="voicemailAudio">Voicemail audio (if this is a voicemail alert)</param>
        /// <param name="isWma">Indicates if the voicemail audio format is WMA</param>
        /// <param name="voicemailCallerId">Voicemail caller ID</param>
        public VoicemailNotificationItem(Context context, NotificationId notificationId, string emailAddress, DateTime notificationTime,
            ReminderItem[] reminderItems, string displayName, byte[] voicemailAudio, bool isWma, string voicemailCallerId) :
            base(context, notificationId, emailAddress, notificationTime, displayName)
        {
            Debug.Assert(reminderItems != null, "reminderItems != null");
            Debug.Assert(reminderItems.Length > 0, "reminderItems.Length > 0"); 

            _reminderItems = new ReadOnlyCollection<ReminderItem>(reminderItems);
            _voicemailAudio = voicemailAudio;
            _isWma = isWma;
            _voicemailCallerId = voicemailCallerId;
        }

        /// <summary>
        /// Produces a string representation of this object.
        /// </summary>
        /// <remarks>
        /// This method is used in logging.
        /// </remarks>
        /// <returns>
        /// The string representation of this object.
        /// </returns>
        public override string ToString()
        {
            return base.ToString() + " Is WMA = " + _isWma.ToString() + " Caller ID = " + _voicemailCallerId;
        }

        /// <summary>
        /// Resolves the custom channels associated with this notification against the specified master list of channels.
        /// </summary>
        /// <param name="masterChannelList">Master list of channels</param>
        public override void ResolveCustomChannels(NotificationChannelList masterChannelList)
        {
            foreach (ReminderItem reminderItem in _reminderItems)
            {
                reminderItem.NotificationChannelList.ResolveCustomChannels(masterChannelList);
            }
        }

        /// <summary>
        /// The reminder information for this notification.
        /// </summary>
        public ReadOnlyCollection<ReminderItem> ReminderItems
        {
            get
            {
                return _reminderItems;
            }
        }

        /// <summary>
        /// Whether or not the voicemail attachment format is WMA. If
        /// false, the attachement is a WAV file.
        /// </summary>
        public bool IsWma
        {
            get
            {
                return _isWma;
            }
        }

        /// <summary>
        /// The raw bytes for a voicemail attachment
        /// </summary>
        public byte[] VoicemailAudio
        {
            get
            {
                return _voicemailAudio;
            }
        }

        /// <summary>
        /// The voicemail caller ID.
        /// </summary>
        public string VoicemailCallerId
        {
            get
            {
                return _voicemailCallerId;
            }
        }

        /// <summary>
        /// Indicates if the channels associated with this notification are enabled.
        /// </summary>
        public override bool IsEnabled
        {
            get
            {
                foreach (ReminderItem reminderItem in _reminderItems)
                {
                    if (reminderItem.IsNotificationEnabled)
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        /// <summary>
        /// Returns the first matching reminder item based on the current presence of the user.
        /// </summary>
        /// <param name="currentPresence">The user's current presence state</param>
        /// <returns>The first matching reminder item or null if there are none</returns>
        public ReminderItem GetReminderItem(PresenceStates currentPresence)
        {
            foreach (ReminderItem reminder in ReminderItems)
            {
                // Only attempt to deliver a voicemail notification if the presence rule on the reminder item is "none" or if
                // the user's current presence matches one of the presence states in the reminder item presence bitflag.
                if (reminder.Presence == PresenceStates.None || (reminder.Presence & currentPresence) != PresenceStates.None)
                {
                    return reminder;
                }
                else
                {
                    Logger.WriteTraceVerbose(Context, "Presence rules do not match. Expected: {0}. Actual: {1}.", reminder.Presence, currentPresence);
                }
            }

            return null;
        }

        /// <summary>
        /// Determines whether or not any of the reminder items will require looking up the user's
        /// presence state.
        /// </summary>
        public bool RequiresPresenceLookup()
        {
            Debug.Assert(ReminderItems != null, "ReminderItems != null");
            Debug.Assert(ReminderItems.Count > 0, "ReminderItems.Count > 0");

            return ReminderItems.Count > 1 || ReminderItems[0].RequiresLookup();
        }
    }
}
